package expression;

import java.util.ArrayList;

import runtime.TypeFactory;
import util.Result;
import util.Scope;
import util.Type;
import exception.ExpressionException;
import exception.ResultException;
import exception.TypeException;
import expression.MultiplicativeExpression.Sign;

/**
 * Klasa odpowiedzialna za przetrzymywanie wyrazenia relacji
 * 
 * @author Jakub Fibinger
 *
 */
public class RelationalExpression extends Expression {

	/**
	 * Ciag znakow relacji
	 */
	private ArrayList<Sign> signs;
	/**
	 * Ciag wyrazen nizszego poziomu
	 */
	private ArrayList<Expression> expressions;
	Type type;
	
	public static enum Sign {
		GT,
		GE,
		LT,
		LE;
	}
	/**
	 * @param scope Zakres zmiennych
	 * @param expression Wyrazenie nizszego poziomu
	 * @throws ExpressionException
	 */
	public RelationalExpression(Scope scope, Expression expression) throws ExpressionException {
		if (expression == null) {
			throw new ExpressionException();
		}
		signs = new ArrayList<Sign>();
		expressions = new ArrayList<Expression>();
		this.type = expression.getResultType();
		expressions.add(expression);
		result = new Result(type, null);
		
	}
	
	/**
	 * @param scope Zakres zmiennych
	 * @param expressions Ciag wyrazen nizszego poziomu
	 * @param signs Ciag znakow relacji
	 * @throws ExpressionException
	 */
	public RelationalExpression(Scope scope, ArrayList<Expression> expressions, ArrayList<Sign> signs) throws ExpressionException {
		TypeFactory tf = TypeFactory.getInstance();
		if ((expressions == null) || (expressions.size() == 0)) {
			throw new ExpressionException();
		} else if (expressions.size()!=signs.size()+1) {
			throw new ExpressionException();
		}
		type = expressions.get(0).getResultType();
		try {
			for(Expression expr:expressions) {
				Type tmpType = expr.getResultType();
				if (!type.equals(tmpType)) {
					if((type.equals(tf.getType("bool")) || tmpType.equals(tf.getType("bool"))) && expressions.size()!=1) {
						throw new ExpressionException();
					} else if (type.equals(tf.getType("double")) || type.equals(tf.getType("int"))){
						if (!tmpType.equals(tf.getType("double")) && !tmpType.equals(tf.getType("int"))){
							throw new ExpressionException();
						} else {
							type = tf.getType("double");
						}
					} else if (type.equals(tf.getType("string")) || type.equals(tf.getType("char"))){
						if (!tmpType.equals(tf.getType("string")) && !tmpType.equals(tf.getType("char"))){
							throw new ExpressionException();
						} else {
							type = tf.getType("string");
						}
					}
				}
			}
			this.signs = signs;
			this.expressions = expressions;
			if(expressions.size()==1) {
				result = new Result(type, null);
			} else {
				result = new Result(tf.getType("bool"), null);
			}
		} catch (TypeException e) {
			// olac
		}
		
	}

	/**
	 * Metoda dodajaca kolejne wyrazenie nizszego poziomu wraz ze znakiem relacji
	 * 
	 * @param expression Wyrazenie nizszego poziomu
	 * @param sign Znak relacji
	 * @throws ExpressionException
	 */
	public void addExpression(Expression expression, Sign sign)
			throws ExpressionException {
		if (expression == null || sign == null) {
			throw new ExpressionException();
		}
		TypeFactory tf = TypeFactory.getInstance();
		try {
			Type tmpType = expression.getResultType();
			if (!type.equals(tmpType)) {
				if(type.equals(tf.getType("bool")) || tmpType.equals(tf.getType("bool"))) {
					throw new ExpressionException();
				} else if (type.equals(tf.getType("double")) || type.equals(tf.getType("int"))){
					if (!tmpType.equals(tf.getType("double")) && !tmpType.equals(tf.getType("int"))){
						throw new ExpressionException();
					}
				} else if (type.equals(tf.getType("string")) || type.equals(tf.getType("char"))){
					if (!tmpType.equals(tf.getType("string")) && !tmpType.equals(tf.getType("char"))){
						throw new ExpressionException();
					}
				}
				signs.add(sign);
				expressions.add(expression);
				result.setType(tf.getType("bool"));
			}
		} catch (TypeException e) {
			// zignoruj
		}
	}
	
	/* (non-Javadoc)
	 * @see expression.Expression#evaluate()
	 */
	@Override
	public Result evaluate() throws ExpressionException {
		if(expressions.size()==1) {
			result.setValue(expressions.get(0).evaluate().getValue());
		} else {
			result.setValue(true);
			Result tmp1, tmp2;
			tmp1 = expressions.get(0).evaluate();
			for(int i=0; (Boolean) result.getValue() && i<signs.size(); i++) {
				tmp2 = expressions.get(i+1).evaluate();
				Type t1, t2;
				t1 = tmp1.getType();
				t2 = tmp2.getType();
				if(signs.get(i).compareTo(Sign.GT)==0) {
					try {
						if(type.equals(TypeFactory.getInstance().getType("int"))){
							result.setValue(((Integer)(tmp1.getValue()))>((Integer)(tmp2.getValue())));
						} else {
							double d1, d2;
							if(t1.equals(TypeFactory.getInstance().getType("int"))){
								d1 = ((Integer)(tmp1.getValue())).doubleValue();
							} else {
								d1 = ((Double)(tmp1.getValue())).doubleValue();
							}
							
						}
					} catch (TypeException e) {
						// TODO Auto-generated catch block
						//e.printStackTrace();
					}
//					System.out.println(signs.get(i));
//					System.out.println(result);
				} else if(signs.get(i).compareTo(Sign.GE)==0) {
					try {
						if(type.equals(TypeFactory.getInstance().getType("int"))){
							result.setValue(((Integer)(tmp1.getValue()))>=((Integer)(tmp2.getValue())));
						} else {
							double d1, d2;
							if(t1.equals(TypeFactory.getInstance().getType("int"))){
								d1 = ((Integer)(tmp1.getValue())).doubleValue();
							} else {
								d1 = ((Double)(tmp1.getValue())).doubleValue();
							}
							
						}
					} catch (TypeException e) {
						// TODO Auto-generated catch block
						//e.printStackTrace();
					}
//					System.out.println(signs.get(i));
//					System.out.println(result);
				} else if(signs.get(i).compareTo(Sign.LT)==0) {
					try {
						if(type.equals(TypeFactory.getInstance().getType("int"))){
							result.setValue(((Integer)(tmp1.getValue()))<((Integer)(tmp2.getValue())));
						} else {
							double d1, d2;
							if(t1.equals(TypeFactory.getInstance().getType("int"))){
								d1 = ((Integer)(tmp1.getValue())).doubleValue();
							} else {
								d1 = ((Double)(tmp1.getValue())).doubleValue();
							}
							
						}
					} catch (TypeException e) {
						// TODO Auto-generated catch block
						//e.printStackTrace();
					}
//					System.out.println(signs.get(i));
//					System.out.println(result);
				} else {
					try {
						if(type.equals(TypeFactory.getInstance().getType("int"))){
							result.setValue(((Integer)(tmp1.getValue()))<=((Integer)(tmp2.getValue())));
						} else {
							double d1, d2;
							if(t1.equals(TypeFactory.getInstance().getType("int"))){
								d1 = ((Integer)(tmp1.getValue())).doubleValue();
							} else {
								d1 = ((Double)(tmp1.getValue())).doubleValue();
							}
							
						}
					} catch (TypeException e) {
						// TODO Auto-generated catch block
						//e.printStackTrace();
					}
//					System.out.println(signs.get(i));
//					System.out.println(result);
				}
				tmp1 = tmp2;
			}
		}
		
		return result;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		StringBuilder sb = new StringBuilder("Relational [ ");
		sb.append(expressions.get(0) + " ");
		int i = 1;
		for(Sign sign : signs)
			sb.append(sign + " " + expressions.get(i++) + " ");
		sb.append("]");
		return sb.toString();
	}

}
